#include "mopplink.h"

MopplinkCore core;
pthread_t thReceieve, thSend, thGuard, thTimeOut, thMock;
pthread_mutex_t mutex, sendMutex;
queue *waitSendQueue = NULL;     //等待发送队列
queue *waitResponseQueue = NULL; //等待接收队列（用于判断请求之后，服务端响应超时，退出请求）

int isocket = 0;
int socketWriteError = 0;
int socketReadError = 0;
int serialNo = 0;
time_t lastSendTime;
char *passowrd = NULL;

int totalSendByte = 0;
int totalRecByte = 0;
int totalTimeCost = 0;

int initSocket();
void sendLoop();
void receiveLoop();

/**
 *
 * 从buffer中读取整型数字
 *
 */
int readInt32FromBuffer(char *buffer, int offset) {
    int number = -1;
    number = buffer[0 + offset];
    number <<= 8;
    number += buffer[1 + offset];
    number <<= 8;
    number += buffer[2 + offset];
    number <<= 8;
    number += buffer[3 + offset];
    return number;
}

char *createRandomPassword(unsigned char length) {
    char *mPassword = (char *)calloc(length, sizeof(char));
    int single;
    srand((unsigned)time(NULL));
    for (int i = 0; i < length; i++) {
        single = rand() % 10 + 48; ////产生48~57的随机数 对应数字0-9
        mPassword[i] = single;
    }

    printf("\n\ncurrent random password is %s\n\n", mPassword);
    return mPassword;
}

void *socketGuardThread(void *arg) {
    struct tcp_info tinfo;
    int len = sizeof(tinfo);
    printf("守护线程启动\n");
    getsockopt(isocket, IPPROTO_TCP, TCP_INFO, &tinfo, (socklen_t *)&len);
    while (1) {
        sleep(50);
        struct tm *info = localtime(&lastSendTime);
        printf("\n%s guard clock in\n", asctime(info));
        getsockopt(isocket, IPPROTO_TCP, TCP_INFO, &tinfo, (socklen_t *)&len);
        if (socketReadError < 0 || socketWriteError < 0 || tinfo.tcpi_state != TCP_ESTABLISHED) {
            printf("aha,catch it!%d,%d\n", socketReadError, socketWriteError);
            if (core.onError != NULL) {
                int x = 9999;
                if (socketReadError < 0) {
                    x = socketReadError;
                }
                else {
                    x = socketWriteError;
                }
                core.onError(x, "系统连接服务器失败，请联系我行客服");
            }
        }
    }
    return arg;
}

void *requestCheckThread(void *arg) {
    printf("待响应队列检查线程启动\n");
    while (1) {
        sleep(5);
        if (waitResponseQueue->first != NULL && waitResponseQueue->rear != NULL) {
            // 1. 定义变量
            node *p = NULL;
            node *next = NULL;

            // 2. 遍历
            pthread_mutex_lock(&sendMutex);
            p = waitResponseQueue->first;
            while (p != NULL) {
                next = p->next;
                if (difftime(time(NULL), p->insertTime) > core.timeout) {
                    //服务端响应超时
                    //直接报错
                    if (core.onTimeout != NULL) {
                        //
                        core.onTimeout(p->serialNo);
                    }
                    //从队列中清除
                    removeTargetNodeQueue(waitResponseQueue, p->serialNo);
                    // p->pre = NULL;
                    // p->next = NULL;
                    free(p->data);
                    free(p);
                }
                else {
                }
                p = next;
            }
            pthread_mutex_unlock(&sendMutex);
        }
        else {
            // printf("待响应队列为空\n");
        }
    }
}

void *mockThread(void *arg) {
    printf("模拟请求线程启动\n");
    while (1) {
        sleep(50);
        cJSON *root = cJSON_CreateObject();
        cJSON *head = cJSON_CreateObject();

        cJSON_AddStringToObject(head, "deviceId", "30586f31f6a7a411");
        cJSON_AddStringToObject(head, "deviceName", "来抓我呀");
        cJSON_AddStringToObject(head, "termType", "1");
        cJSON_AddNumberToObject(head, "appVersion", 320);
        cJSON_AddStringToObject(head, "appVersionName", "3.2.0(576fd1f)");
        cJSON_AddStringToObject(head, "osVersion", "11");
        cJSON_AddBoolToObject(head, "isRootDevice", 0);

        cJSON_AddItemToObject(root, "head", head);

        printf("mock send data body is %s\n\n", cJSON_Print(root));
        RequestPackage rq = {
            .app = 801,
            .service = "perbank",
            .action = "mBank/GenImgCode",
            .body = cJSON_PrintUnformatted(root),
            .session = NULL,
            .user = NULL,
        };
        int sq1 = moppSend(rq);
        printf("\nmock send data 1;sq is %d\n", sq1);
        cJSON_free(root);
        cJSON_free(head);
    }
    return arg;
}

void *heartBeatThread(void *arg) {}

void *receieveThread(void *arg) {
    receiveLoop();
    return arg;
}

void *socketInitSendThread(void *arg) {
    printf("This is socket thread and arg = %d.\n", *(int *)arg);
    initSocket();
    sendLoop();
    return arg;
}

#define MAXLINE 4096

int initSocket() {
    //创建套接字
    isocket = socket(AF_INET, SOCK_STREAM, 0);
    printf("socket created\n");
    //服务器的ip为本地，端口号6033
    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = inet_addr(core.ip);
    serv_addr.sin_port = htons(core.port);
    //向服务器发送连接请求
    connect(isocket, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
    printf("server connected\n");
    //发送并接收数据
    char buffer[1024];

    int packageLength = 32;
    char handshake[28];

    handshake[0] = 0x01;
    handshake[1] = 0x01;
    handshake[2] = 0x01;
    handshake[3] = 0x01;

    printf("password is %s\nlen is %d;\n", passowrd, strlen(passowrd));
    for (int i = 0; i < strlen(passowrd); i++) {
        handshake[i + 4] = passowrd[i];
    }
    // printf("\n handshake data:\n");
    // for (int i = 0; i < strlen(passowrd) + 4; i++) {
    //     printf("%d->0x%.2X\n", i, handshake[i]);
    // }
    // printf("\n");

    Encrypt encrypt = core.asymmetricEncryption(handshake, core.keyPath);
    packageLength = encrypt.len;
    buffer[0] = (char)(packageLength >> 24);
    buffer[1] = (char)(packageLength >> 16);
    buffer[2] = (char)(packageLength >> 8);
    buffer[3] = (char)(packageLength);

    for (int i = 0; i < encrypt.len; i++) {
        // char da = *(encryptPassword++);
        // printf("0x%.2X,", da);
        buffer[4 + i] = encrypt.data[i];
    }

    printf("\n encrypt data:\n");
    for (int i = 0; i < 4 + packageLength; i++) {
        // printf("%.2X,", buffer[i]);
    }
    printf("\n");

    // buffer[240]=0xaa;
    printf("\n data length to send %d\n", packageLength);

    write(isocket, buffer, packageLength + 4);
    printf("hand shake package send\n");
    lastSendTime = time(NULL);
    //启动接收线程
    int *thread_ret = NULL;
    int arg = 2;
    int ret2 = pthread_create(&thReceieve, NULL, receieveThread, &arg);
    if (ret2 != 0) {
        printf("Create receieve thread error!\n");
        return -1;
    }
}

char *makePackage(char *request, int requestLen, int serialNo, char version, char flag) {
    // printf("strlen(request) = %d \n", strlen(request));
    // int requestLen = strlen(request);
    char *buffer = (char *)calloc(requestLen + 16 + 4 + 1 + 1, sizeof(char));
    int offset = 16;
    buffer[offset] = (char)(serialNo >> 24);
    offset++;
    buffer[offset] = (char)(serialNo >> 16);
    offset++;
    buffer[offset] = (char)(serialNo >> 8);
    offset++;
    buffer[offset] = (char)(serialNo);
    offset++;
    buffer[offset] = (char)(version);
    offset++;
    buffer[offset] = (char)(flag);
    offset++;
    memcpy(buffer + offset, request, strlen(request));

    int dataLength = strlen(request) + 4 + 1 + 1;
    char *buffer2 = (char *)calloc(dataLength, sizeof(char));
    memcpy(buffer2, buffer + 16, dataLength);

    char *decrypt = core.digest(buffer2, dataLength);
    memcpy(buffer, decrypt, 16);
    free(buffer2);
    free(decrypt);
    return buffer;
}

#define CRC_LENGTH 16
char checkMatch(char *buffer, int bufferLength) {
    char *str = buffer + 16 + 4 + 1 + 1;

    int mPackageLength = bufferLength - CRC_LENGTH;
    int dataLenght = mPackageLength - 4 - 1 - 1;

    unsigned char *buffer2 = (unsigned char *)calloc(mPackageLength, sizeof(char));
    memcpy(buffer2, buffer + CRC_LENGTH, mPackageLength);

    //计算摘要
    char *decrypt = core.digest(buffer2, mPackageLength);

    char match = 1;
    for (char i = 0; i < 16; i++) {
        match = decrypt[i] == buffer[i];
        // printf("\t0x%.2X,\t0x%.2X\n", buffer[i], decrypt[i]);
        if (!match) {
            free(decrypt);
            printf("data receieved crc check can't pass!");
            return match;
        }
    }
    free(decrypt);
    return match;
}

void doSend(char *waitSendData, int dataLength, int serialNo) {
    int length = 16 + dataLength + 4 + 1 + 1;
    char *buffer2 = makePackage(waitSendData, dataLength, serialNo, 1, 0);

    // Encrypt encrypt = desEncrypt(passowrd, buffer2, length);
    Encrypt encrypt = core.symmetricEncryption(passowrd, buffer2, length);

    char *waitSend = (char *)calloc(encrypt.len + 4, sizeof(char));
    int packageLength = encrypt.len;
    waitSend[0] = (char)(packageLength >> 24);
    waitSend[1] = (char)(packageLength >> 16);
    waitSend[2] = (char)(packageLength >> 8);
    waitSend[3] = (char)(packageLength);
    memcpy(waitSend + 4, encrypt.data, packageLength);

    socketWriteError = write(isocket, waitSend, packageLength + 4);

    totalSendByte += (packageLength + 4);

    free(buffer2);
    free(waitSend);
    free(encrypt.data);
}

void sendLoop() {
    //发送并接收数据
    char buffer[4] = {0x00, 0x00, 0x00, 0x00};
    printf("send loop start!\n");
    printf("socketWriteError = %d\n", socketWriteError);
    struct tcp_info info;
    int len = sizeof(info);
    getsockopt(isocket, IPPROTO_TCP, TCP_INFO, &info, (socklen_t *)&len);
    while (info.tcpi_state == TCP_ESTABLISHED) {
        getsockopt(isocket, IPPROTO_TCP, TCP_INFO, &info, (socklen_t *)&len);
        node *msg = NULL;
        //出栈
        //线程安全
        pthread_mutex_lock(&mutex);
        msg = queuePop(waitSendQueue);
        pthread_mutex_unlock(&mutex);
        if (msg != NULL) {
            if (msg->serialNo < 0 || msg->length <= 0) {
                printf("message queue error\n");
            }
            else {
                // printf("socket flag 5\n");

                // printf("sn:%d\n", msg->serialNo);
                // printf("length:%d\n", msg->length);
                // printf("data:%s\n", msg->data);

                doSend(msg->data, msg->length, msg->serialNo);
                // printf("假装发送数据\n");//用于测试消息超时

                //
                pthread_mutex_lock(&sendMutex);
                queueInsert(waitResponseQueue, msg->serialNo, msg->length, msg->data);
                pthread_mutex_unlock(&sendMutex);

                free(msg->data);
                free(msg);
                //记录本次数据发送时间，用于心跳判断
                lastSendTime = time(NULL);
            }
        }
        else {
            //计算心跳时间
            int seconds = difftime(time(NULL), lastSendTime);
            if (seconds >= core.heartBeatTime) {
                write(isocket, buffer, 4);
                lastSendTime = time(NULL);
                struct tm *info = localtime(&lastSendTime);
                printf("%s heart beat send\n", asctime(info));
            }
        }
        usleep(1000);
    }
    printf("send loop exit!\n");
}

void receiveLoop() {
    char *buffer = NULL;
    char *dataBuffer = NULL;
    printf("receieve thread start!\n");
    int waitReadLength = 0;
    int getHead = 0;
    int haveRead = 0;
    unsigned char head[30] = {0, 0, 0, 0}; //处理黏包情况，多申请点内存
    buffer = (char *)calloc(30, sizeof(char));
    Encrypt encrypt;
    //一旦读取报错，就退出
    int optval;
    int optlen = sizeof(int);
    struct tcp_info info;
    int len = sizeof(info);
    getsockopt(isocket, IPPROTO_TCP, TCP_INFO, &info, (socklen_t *)&len);
    while ((info.tcpi_state == TCP_ESTABLISHED)) {
        int read_len = read(isocket, buffer, sizeof(buffer) - 1);
        getsockopt(isocket, IPPROTO_TCP, TCP_INFO, &info, (socklen_t *)&len);
        socketReadError = read_len;
        //调试用的代码，很有用
        // printf("\n\nread data from server. \nread_len:%d;  \ngetHead:%d;  \nreaded:%d;  \nwaitReadLength:%d;\n\n\n", read_len, getHead, haveRead,
        // waitReadLength);
        if (getHead) {
            memcpy(dataBuffer + haveRead, buffer, read_len);
            haveRead += read_len;
            if (haveRead >= waitReadLength) {
                printf("\n\nget all data ;\treaded=%d;waitReadLength=%d\n\n", haveRead, waitReadLength);
                if (waitReadLength % 8 == 0 || 1) {
                    totalRecByte += waitReadLength;
                    encrypt = core.symmetricDecryption(passowrd, dataBuffer, waitReadLength);
                    if (checkMatch(encrypt.data, encrypt.len)) {
                        int idataLength = encrypt.len - (16 + 4 + 1 + 1);
                        char *data = calloc(idataLength + 1, sizeof(char)); // encrypt.data + 16 + 4 + 1 + 1
                        memcpy(data, encrypt.data + 16 + 4 + 1 + 1, idataLength);
                        data[idataLength] = 0; //截断字符串
                        char sq = readInt32FromBuffer(encrypt.data, 16);
                        printf("decrypt data,length is %d\n", encrypt.len);
                        printf("decrypt str,length is %d, strlen(data) is %d\n", idataLength, strlen(data));
                        printf("decrypt content,content is :\n%s\n", data);

                        pthread_mutex_lock(&sendMutex);
                        node *p = getTargetNodeInQueue(waitResponseQueue, sq);
                        if (p != NULL) {
                            printf("收到响应报文，移除待接收队列\n");
                            //从队列中清除
                            int ret = removeTargetNodeQueue(waitResponseQueue, p->serialNo);
                            // p->pre = NULL;
                            // p->next = NULL;
                            free(p->data);
                            free(p);
                            if (core.onMsgArrived != NULL) {
                                core.onMsgArrived(sq, data);
                            }
                        }
                        else {
                            printf("收到响应报文，待接收队列中为空\n");
                        }
                        pthread_mutex_unlock(&sendMutex);

                        free(data);
                    }
                    free(encrypt.data);
                }
                else {
                    printf("\n\n receieved data is invaid!\n\n");
                }
                int left = haveRead - waitReadLength;
                if (left > 0) {
                    //黏包了，需要处理下
                    if (left >= 4) {
                        memcpy(head, dataBuffer + waitReadLength, left);
                        waitReadLength = readInt32FromBuffer(head, 0);
                        haveRead = left - 4;
                        free(dataBuffer); //先释放
                        printf("释放了内存1，大小：%d\n", waitReadLength + 10);
                        if (waitReadLength > 0) {
                            dataBuffer = (char *)calloc(waitReadLength + 10, sizeof(char));
                            if (haveRead > 0) {
                                memcpy(dataBuffer, head + 4, haveRead);
                            }
                            else {
                            }
                            getHead = 1;
                        }
                        else {
                            haveRead = 0;
                            waitReadLength = 0;
                            getHead = 0;
                            printf("received server heartbeat response 2\n");
                        }
                    }
                    else {
                        //不够头部包，继续接收
                        memcpy(head, dataBuffer + waitReadLength, left);
                        haveRead = left;
                        waitReadLength = 0;
                        getHead = 0;
                        free(dataBuffer); //释放
                        printf("释放了内存2，大小：%d\n", waitReadLength + 10);
                    }
                }
                else {
                    haveRead = 0;
                    waitReadLength = 0;
                    getHead = 0;
                    free(dataBuffer);
                    printf("释放了内存3\n");
                }
            }
        }
        else {
            memcpy(head + haveRead, buffer, read_len);
            int use = 4 - haveRead;
            int left = read_len - use;
            haveRead += read_len;
            // printf("received head, \tread_len=%d; \tuse=%d; \tleft=%d; \treaded=%d\n", read_len, use, left, haveRead);
            if (haveRead >= 4) {
                waitReadLength = readInt32FromBuffer(head, 0);
                // printf("get head,waiting read is:%d\n", waitReadLength);
                if (waitReadLength > 0) {
                    dataBuffer = (char *)calloc(waitReadLength + 10, sizeof(char));
                    printf("申请了内存，用于接收，大小是%d\n", waitReadLength + 10);
                    if (haveRead > 4) {
                        int dataLength = left;
                        memcpy(dataBuffer, buffer + use, dataLength);

                        // printf("\n\nmore read buffer is %d:\n", dataLength);
                        // for (int i = 0; i < dataLength; i++) {
                        //     printf("0x%.2X,", dataBuffer[i]);
                        // }
                        // printf("\n\n\n");

                        haveRead = dataLength;
                    }
                    else {
                        haveRead = 0;
                    }
                    getHead = 1;
                }
                else {
                    haveRead = 0;
                    waitReadLength = 0;
                    getHead = 0;
                    printf("received server heartbeat response 1\n");
                }
            }
        }
    }
    free(buffer);
    printf("receieve thread exit!\n");
}

int coreSend(char *waitSendData, int dataLength) {
    int ret = 0;
    //入栈
    //线程安全
    pthread_mutex_lock(&mutex);

    serialNo++;
    ret = serialNo;
    char *data = (char *)calloc(dataLength, sizeof(char));
    memcpy(data, waitSendData, dataLength);
    queueInsert(waitSendQueue, serialNo, dataLength, data);

    pthread_mutex_unlock(&mutex);

    return ret;
}

int moppSend(RequestPackage requestPackage) {
    cJSON *root = cJSON_CreateObject();
    cJSON *body = cJSON_CreateObject();
    cJSON_AddItemToObject(root, "app", cJSON_CreateNumber(requestPackage.app));         //根节点下添加
    cJSON_AddItemToObject(root, "service", cJSON_CreateString(requestPackage.service)); //根节点下添加
    cJSON_AddItemToObject(root, "action", cJSON_CreateString(requestPackage.action));   //根节点下添加
    if (requestPackage.body != NULL) {
        cJSON_free(body);
        body = cJSON_Parse(requestPackage.body);
    }
    cJSON_AddItemToObject(root, "body", body);
    if (requestPackage.user != NULL) {
        cJSON_AddItemToObject(root, "user", cJSON_CreateString(requestPackage.user)); //根节点下添加
    }
    if (requestPackage.session != NULL) {
        cJSON_AddItemToObject(root, "session", cJSON_CreateString(requestPackage.session));
    }

    char *waitSendData = cJSON_PrintUnformatted(root);
    printf("\nwait send json data:\n%s", cJSON_Print(root));
    int dataLength = strlen(waitSendData);

    int sq = coreSend(waitSendData, dataLength);

    cJSON_free(root);
    cJSON_free(body);
    return sq;
}

int arg = 1;
int *thread_ret = NULL;

void zygota() {
    //创建随机密码
    passowrd = createRandomPassword(core.isGM ? 32 : 24);
    printf("password is %s\nlen is %d;\n", passowrd, strlen(passowrd));

    //创建socket启动和发送线程，并在内部创建接收线程
    int ret2 = pthread_create(&thSend, NULL, socketInitSendThread, &arg);
    if (ret2 != 0) {
        printf("Create thread error!\n");
    }
}

int moppStart(MopplinkCore mopplinkCore) {
    core = mopplinkCore;

    if (core.asymmetricEncryption == NULL) {
        return -1;
    }
    if (core.symmetricEncryption == NULL) {
        return -2;
    }
    if (core.symmetricDecryption == NULL) {
        return -3;
    }
    if (core.digest == NULL) {
        return -4;
    }
    if (core.ip == NULL) {
        return -5;
    }
    if (core.port <= 0) {
        return -6;
    }

    //创建消息队列
    waitSendQueue = (queue *)calloc(1, sizeof(queue));
    //待响应队列
    waitResponseQueue = (queue *)calloc(1, sizeof(queue));

    //创建socket启动和发送线程，并在内部创建接收线程
    zygota();

    //守护线程
    int ret3 = pthread_create(&thGuard, NULL, socketGuardThread, &arg);
    if (ret3 != 0) {
        printf("Create guard thread error!\n");
        return -1;
    }

    //超时响应线程
    int ret4 = pthread_create(&thTimeOut, NULL, requestCheckThread, &arg);
    if (ret4 != 0) {
        printf("Create timeout thread error!\n");
        return -1;
    }

    //测试线程
    int ret5 = pthread_create(&thMock, NULL, mockThread, &arg);
    if (ret5 != 0) {
        printf("Create mock thread error!\n");
        return -1;
    }

    printf("This is the main process.\n");
    pthread_join(thGuard, (void **)&thread_ret);
    printf("thread_ret = %d.\n", *thread_ret);
    close(isocket);
    free(passowrd);

    if (core.onError != NULL) {
        core.onError(socketWriteError < 0 ? socketWriteError : socketReadError, "我挂了，还没找到原因");
    }

    return 0;
}